package de.justphil.tcg.tcgserver.rest.db;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import org.slf4j.Logger;

import de.justphil.tcg.tcgserver.commons.config.RestConfig;
import de.justphil.tcg.tcgserver.commons.domain.Card;
import de.justphil.tcg.tcgserver.commons.domain.CardInstance;
import de.justphil.tcg.tcgserver.commons.domain.Deck;
import de.justphil.tcg.tcgserver.commons.domain.Property;
import de.justphil.tcg.tcgserver.commons.domain.User;
import de.justphil.tcg.tcgserver.commons.domain.User.ManagerId;
import static de.justphil.tcg.tcgserver.commons.logging.TCGLogging.*;

public class MockDataCreator {
	private static final Logger log = logger(MockDataCreator.class);

	private EntityManagerFactory factory;
	
	
	public MockDataCreator(EntityManagerFactory factory) {
		this.factory = factory;
	}
	
	
	public void insertMockData() {
		// create entity manager
		EntityManager em = factory.createEntityManager();
		
		// insert admin users
		User location = insertLocationUser(em);
		
		insertGameDrawUser(em);
		
		// insert users + dummy games
		User[] users = insertUsers(em);
		
		/*
		User[] newUsers = Arrays.copyOf(users, users.length + 2);
		newUsers[newUsers.length-2] = location;
		newUsers[newUsers.length-1] = location; // put location user two times in order to create two freebies for each card
		*/
		
		// insert cards
		List<Card> cards = insertCards(em, users);
		
		// insert freebie cards
		insertFreebieCards(em, cards, location);
		
		// insert freebie card at Martin's house
		insertFreebieCardAtPosition(em, cards, location, 51.194505173327535, 6.768016798522238);
		
		// insert freebie card at Phil's house
		insertFreebieCardAtPosition(em, cards, location, 51.4525761702161, 7.0342326164245605);
		
		// insert freebie card at IEM
		insertFreebieCardAtPosition(em, cards, location, 51.47559293485953, 7.011744976043701);

		// close entity manager
		em.close();
	}
	
	private User insertLocationUser(EntityManager em) {
		// begin transactions
		em.getTransaction().begin();
		
		// create entities
		User location = new User("tcg_manager_location", "e25f983a6dfd43692cc5365d5c6aba3d", true);
		
		// persist entities
		em.persist(location);
		
		// commit transactions
		em.getTransaction().commit();
		
		return location;
	}
	
	private User insertGameDrawUser(EntityManager em) {
		// begin transactions
		em.getTransaction().begin();
		
		// create entities
		User location = new User("tcg_manager_game_draw", "e25f983a6dfd43692cc5365d5c6aba3d", true);
		
		// persist entities
		em.persist(location);
		
		// commit transactions
		em.getTransaction().commit();
		
		return location;
	}
	
	private User[] insertUsers(EntityManager em) {
		// begin transactions
		em.getTransaction().begin();
		
		// create entities
		User phil	= new User("phil", "e120ea280aa50693d5568d0071456460", false);
		User martin	= new User("martin", "e120ea280aa50693d5568d0071456460", false);
		User john	= new User("john", "e120ea280aa50693d5568d0071456460", false);
		User james	= new User("james", "e120ea280aa50693d5568d0071456460", false);
		
		/*
		Game g1 	= new Game(Game.State.PENDING);
		Game g2 	= new Game(Game.State.PENDING);
		Game g3 	= new Game(Game.State.PENDING);
		Game g4 	= new Game(Game.State.PENDING);
		*/
		
		// persist entities
		em.persist(phil);
		em.flush();
		
		em.persist(martin);
		em.flush();
		
		em.persist(john);
		em.flush();
		
		em.persist(james);
		em.flush();
		
		/*
		em.persist(g1);
		em.persist(g2);
		em.persist(g3);
		em.persist(g4);
		*/
		
		// associate entities
		/*
		phil.getGameList().add(g1);
		phil.getGameList().add(g3);
		martin.getGameList().add(g2);
		martin.getGameList().add(g4);
		john.getGameList().add(g1);
		john.getGameList().add(g3);
		james.getGameList().add(g2);
		james.getGameList().add(g4);
		
		g1.getPlayerList().add(phil);
		g1.getPlayerList().add(john);
		g1.setWinner(phil);
		
		g3.getPlayerList().add(phil);
		g3.getPlayerList().add(john);
		g3.setWinner(john);
		
		g2.getPlayerList().add(martin);
		g2.getPlayerList().add(james);
		g2.setWinner(martin);
		
		g4.getPlayerList().add(martin);
		g4.getPlayerList().add(james);
		g4.setWinner(james);
		*/
		
		// commit transactions
		em.getTransaction().commit();
		
		return new User[] { phil, martin, john, james };
	}
	
	private List<Card> insertCards(EntityManager em, User... users) {
		// begin transactions
		em.getTransaction().begin();
		
		// init list that will be returned
		List<Card> out = new ArrayList<Card>();
		
		// create entities
		Card bigfoot	= new Card("Bigfoot", 		4, 5, 2, 2);
		Card bullfrog	= new Card("Bullfrog", 		6, 3, 3, 3);
		Card cerberus	= new Card("Cerberus", 		3, 3, 3, 5);
		Card elephant	= new Card("Elephant", 	   10, 5, 2, 3);
		Card elfMage	= new Card("Elf Mage", 		3, 3, 3, 3);
		Card goblin		= new Card("Goblin", 		3, 4, 4, 4);
		Card hedgehog	= new Card("Hedgehog",		2, 2, 5, 2);
		Card magicRock	= new Card("Magic Rock",	3, 0, 8, 0);
		Card roadrunner = new Card("Roadrunner", 	3, 3, 1, 10);
		Card snake		= new Card("Snake", 		2, 8, 2, 3);
		Card wildBore	= new Card("Wild Bore", 	4, 4, 3, 4);
		Card wolf		= new Card("Wolf", 			3, 4, 3, 3);
		
		Property attacker				= new Property("Attacker");
		Property deathExplosion			= new Property("Death Explosion");
		Property multiAttack			= new Property("Multi Attack");
		Property paralyzingAttack		= new Property("Paralyzing Attack");
		Property sleepAttack			= new Property("Sleep Attack");
		Property attackDamageReflection	= new Property("Attack Damage Reflection");
		Property regenerationAura		= new Property("Regeneration Aura");
		Property resistanceAura			= new Property("Resistance Aura");
		Property poisonResistant		= new Property("Poison Resistant");
		Property lazy					= new Property("Lazy");
		Property poisonous				= new Property("Poisonous");
		Property furyAura				= new Property("Fury Aura");
		Property retaliation			= new Property("Retaliation");
		Property fury					= new Property("Fury");
		Property paralyzed				= new Property("Paralyzed");
		Property damage					= new Property("Damage");
		Property poisonDefenseLoss		= new Property("Poison Defense Loss");
		Property resistance				= new Property("Resistance");
		Property regeneration			= new Property("Regeneration");
		Property sleeping				= new Property("Sleeping");
		
		/* ############################################################################################ */
		/* ############################################################################################ */
		/* ############################################################################################ */
		
		// persist entities
		
		// Cards
		em.persist(bigfoot);
		em.flush();
		
		em.persist(bullfrog);
		em.flush();
		
		em.persist(cerberus);
		em.flush();
		
		em.persist(elephant);
		em.flush();
		
		em.persist(elfMage);
		em.flush();
		
		em.persist(goblin);
		em.flush();
		
		em.persist(hedgehog);
		em.flush();
		
		em.persist(magicRock);
		em.flush();
		
		em.persist(roadrunner);
		em.flush();
		
		em.persist(snake);
		em.flush();
		
		em.persist(wildBore);
		em.flush();
		
		em.persist(wolf);
		em.flush();
		
		
		// Properties
		em.persist(attacker);
		em.flush();
		
		em.persist(deathExplosion);
		em.flush();
		
		em.persist(multiAttack);
		em.flush();
		
		em.persist(paralyzingAttack);
		em.flush();
		
		em.persist(sleepAttack);
		em.flush();
		
		em.persist(attackDamageReflection);
		em.flush();
		
		em.persist(regenerationAura);
		em.flush();
		
		em.persist(resistanceAura);
		em.flush();
		
		em.persist(poisonResistant);
		em.flush();
		
		em.persist(lazy);
		em.flush();
		
		em.persist(poisonous);
		em.flush();
		
		em.persist(furyAura);
		em.flush();
		
		em.persist(retaliation);
		em.flush();
		
		em.persist(fury);
		em.flush();
		
		em.persist(paralyzed);
		em.flush();
		
		em.persist(damage);
		em.flush();
		
		em.persist(poisonDefenseLoss);
		em.flush();
		
		em.persist(resistance);
		em.flush();
		
		em.persist(regeneration);
		em.flush();
		
		em.persist(sleeping);
		em.flush();
		
		
		
		// associate entities (card -> property)
		bigfoot.getProperties().add(attacker);
		
		bullfrog.getProperties().add(attacker);
		bullfrog.getProperties().add(deathExplosion);
		
		cerberus.getProperties().add(attacker);
		cerberus.getProperties().add(multiAttack);
		
		elephant.getProperties().add(attacker);
		
		elfMage.getProperties().add(attacker);
		elfMage.getProperties().add(paralyzingAttack);
		
		goblin.getProperties().add(attacker);
		goblin.getProperties().add(sleepAttack);
		
		hedgehog.getProperties().add(attacker);
		hedgehog.getProperties().add(attackDamageReflection);
		
		magicRock.getProperties().add(regenerationAura);
		magicRock.getProperties().add(resistanceAura);
		magicRock.getProperties().add(poisonResistant);
		magicRock.getProperties().add(lazy);
		
		roadrunner.getProperties().add(attacker);
		
		snake.getProperties().add(attacker);
		snake.getProperties().add(poisonous);
		snake.getProperties().add(poisonResistant);
		
		wildBore.getProperties().add(attacker);
		wildBore.getProperties().add(furyAura);
		
		wolf.getProperties().add(attacker);
		wolf.getProperties().add(retaliation);
		
		// associate entities (property -> card)
		/*
		attacker.getCardList().add(bigfoot);
		attacker.getCardList().add(bullfrog);
		attacker.getCardList().add(cerberus);
		attacker.getCardList().add(elephant);
		attacker.getCardList().add(elfMage);
		attacker.getCardList().add(goblin);
		attacker.getCardList().add(hedgehog);
		attacker.getCardList().add(roadrunner);
		attacker.getCardList().add(snake);
		attacker.getCardList().add(wildBore);
		attacker.getCardList().add(wolf);
		
		deathExplosion.getCardList().add(bullfrog);
		
		multiAttack.getCardList().add(cerberus);
		
		paralyzingAttack.getCardList().add(elfMage);
		
		sleepAttack.getCardList().add(goblin);
		
		attackDamageReflection.getCardList().add(hedgehog);
		
		regenerationAura.getCardList().add(magicRock);
		
		resistanceAura.getCardList().add(magicRock);
		
		poisonResistant.getCardList().add(magicRock);
		poisonResistant.getCardList().add(snake);
		
		lazy.getCardList().add(magicRock);
		
		poisonous.getCardList().add(snake);
		
		furyAura.getCardList().add(wildBore);
		
		retaliation.getCardList().add(wolf);
		*/
		
		// create & associate card instances with users
		CardInstance ciBigfoot;
		CardInstance ciBullfrog;
		CardInstance ciCerberus;
		CardInstance ciElephant;
		CardInstance ciElfMage;
		CardInstance ciGoblin;
		CardInstance ciHedgehog;
		CardInstance ciMagicRock;
		CardInstance ciRoadrunner;
		CardInstance ciSnake;
		CardInstance ciWildBore;
		CardInstance ciWolf;
		
		for(User user : users) {
			log.debug("Creating card instances and decks for '{}'...", user.getUsername());
			
			if (user.getId() == ManagerId.LOCATION) {
				ciBigfoot		= new CardInstance(51.4525661, 7.034474, user.getId(), bigfoot.getId());
				ciBullfrog		= new CardInstance(51.4525661, 7.034474, user.getId(), bullfrog.getId());
				ciCerberus		= new CardInstance(51.4525661, 7.034474, user.getId(), cerberus.getId());
				ciElephant		= new CardInstance(51.4525661, 7.034474, user.getId(), elephant.getId());
				ciElfMage		= new CardInstance(51.4525661, 7.034474, user.getId(), elfMage.getId());
				ciGoblin		= new CardInstance(51.4525661, 7.034474, user.getId(), goblin.getId());
				ciHedgehog		= new CardInstance(51.4525661, 7.034474, user.getId(), hedgehog.getId());
				ciMagicRock		= new CardInstance(51.4525661, 7.034474, user.getId(), magicRock.getId());
				ciRoadrunner	= new CardInstance(51.4525661, 7.034474, user.getId(), roadrunner.getId());
				ciSnake			= new CardInstance(51.4525661, 7.034474, user.getId(), snake.getId());
				ciWildBore		= new CardInstance(51.4525661, 7.034474, user.getId(), wildBore.getId());
				ciWolf			= new CardInstance(51.4525661, 7.034474, user.getId(), wolf.getId());
			}
			else {
				ciBigfoot		= new CardInstance(0, 0, user.getId(), bigfoot.getId());
				ciBullfrog		= new CardInstance(0, 0, user.getId(), bullfrog.getId());
				ciCerberus		= new CardInstance(0, 0, user.getId(), cerberus.getId());
				ciElephant		= new CardInstance(0, 0, user.getId(), elephant.getId());
				ciElfMage		= new CardInstance(0, 0, user.getId(), elfMage.getId());
				ciGoblin		= new CardInstance(0, 0, user.getId(), goblin.getId());
				ciHedgehog		= new CardInstance(0, 0, user.getId(), hedgehog.getId());
				ciMagicRock		= new CardInstance(0, 0, user.getId(), magicRock.getId());
				ciRoadrunner	= new CardInstance(0, 0, user.getId(), roadrunner.getId());
				ciSnake			= new CardInstance(0, 0, user.getId(), snake.getId());
				ciWildBore		= new CardInstance(0, 0, user.getId(), wildBore.getId());
				ciWolf			= new CardInstance(0, 0, user.getId(), wolf.getId());
			}
			
			em.persist(ciBigfoot);
			em.persist(ciBullfrog);
			em.persist(ciCerberus);
			em.persist(ciElephant);
			em.persist(ciElfMage);
			em.persist(ciGoblin);
			em.persist(ciHedgehog);
			em.persist(ciMagicRock);
			em.persist(ciRoadrunner);
			em.persist(ciSnake);
			em.persist(ciWildBore);
			em.persist(ciWolf);
			
			em.flush();
			
			// user -> card instance
			user.getCardInstances().add(ciBigfoot);
			user.getCardInstances().add(ciBullfrog);
			user.getCardInstances().add(ciCerberus);
			user.getCardInstances().add(ciElephant);
			user.getCardInstances().add(ciElfMage);
			user.getCardInstances().add(ciGoblin);
			user.getCardInstances().add(ciHedgehog);
			user.getCardInstances().add(ciMagicRock);
			user.getCardInstances().add(ciRoadrunner);
			user.getCardInstances().add(ciSnake);
			user.getCardInstances().add(ciWildBore);
			user.getCardInstances().add(ciWolf);
			
			// card instance -> user
			/*
			ciBigfoot.setUserId(user.getId());
			ciBullfrog.setUserId(user.getId());
			ciCerberus.setUserId(user.getId());
			ciElephant.setUserId(user.getId());
			ciElfMage.setUserId(user.getId());
			ciGoblin.setUserId(user.getId());
			ciHedgehog.setUserId(user.getId());
			ciMagicRock.setUserId(user.getId());
			ciRoadrunner.setUserId(user.getId());
			ciSnake.setUserId(user.getId());
			ciWildBore.setUserId(user.getId());
			ciWolf.setUserId(user.getId());
			*/
			
			/*
			ciBigfoot.setUser(user);
			ciBullfrog.setUser(user);
			ciCerberus.setUser(user);
			ciElephant.setUser(user);
			ciElfMage.setUser(user);
			ciGoblin.setUser(user);
			ciHedgehog.setUser(user);
			ciMagicRock.setUser(user);
			ciRoadrunner.setUser(user);
			ciSnake.setUser(user);
			ciWildBore.setUser(user);
			ciWolf.setUser(user);
			*/
			
			
			// card instance -> card
			/*
			ciBigfoot.setCardId(bigfoot.getId());
			ciBullfrog.setCardId(bullfrog.getId());
			ciCerberus.setCardId(cerberus.getId());
			ciElephant.setCardId(elephant.getId());
			ciElfMage.setCardId(elfMage.getId());
			ciGoblin.setCardId(goblin.getId());
			ciHedgehog.setCardId(hedgehog.getId());
			ciMagicRock.setCardId(magicRock.getId());
			ciRoadrunner.setCardId(roadrunner.getId());
			ciSnake.setCardId(snake.getId());
			ciWildBore.setCardId(wildBore.getId());
			ciWolf.setCardId(wolf.getId());
			*/
			
			/*
			ciBigfoot.setCard(bigfoot);
			ciBullfrog.setCard(bullfrog);
			ciCerberus.setCard(cerberus);
			ciElephant.setCard(elephant);
			ciElfMage.setCard(elfMage);
			ciGoblin.setCard(goblin);
			ciHedgehog.setCard(hedgehog);
			ciMagicRock.setCard(magicRock);
			ciRoadrunner.setCard(roadrunner);
			ciSnake.setCard(snake);
			ciWildBore.setCard(wildBore);
			ciWolf.setCard(wolf);
			*/
			
			// card -> card instance
			bigfoot.getCardInstances().add(ciBigfoot);
			bullfrog.getCardInstances().add(ciBullfrog);
			cerberus.getCardInstances().add(ciCerberus);
			elephant.getCardInstances().add(ciElephant);
			elfMage.getCardInstances().add(ciElfMage);
			goblin.getCardInstances().add(ciGoblin);
			hedgehog.getCardInstances().add(ciHedgehog);
			magicRock.getCardInstances().add(ciMagicRock);
			roadrunner.getCardInstances().add(ciRoadrunner);
			snake.getCardInstances().add(ciSnake);
			wildBore.getCardInstances().add(ciWildBore);
			wolf.getCardInstances().add(ciWolf);
			
			if (user.getId() != ManagerId.LOCATION) {
				// create decks
				Deck survivalDeck		= new Deck("Survival of the Fittest", user.getId());
				Deck renaissanceDeck	= new Deck("Renaissance", user.getId());
				
				em.persist(survivalDeck);
				em.persist(renaissanceDeck);
				
				em.flush();
				
				// user -> deck
				user.getDecks().add(survivalDeck);
				user.getDecks().add(renaissanceDeck);
				
				// deck -> user
				/*
				survivalDeck.setUserId(user.getId());
				renaissanceDeck.setUserId(user.getId());
				*/
				
				// deck -> card instance
				survivalDeck.getCardInstances().add(ciBigfoot);
				survivalDeck.getCardInstances().add(ciBullfrog);
				survivalDeck.getCardInstances().add(ciCerberus);
				survivalDeck.getCardInstances().add(ciElephant);
				survivalDeck.getCardInstances().add(ciElfMage);
				survivalDeck.getCardInstances().add(ciGoblin);
				
				renaissanceDeck.getCardInstances().add(ciHedgehog);
				renaissanceDeck.getCardInstances().add(ciMagicRock);
				renaissanceDeck.getCardInstances().add(ciRoadrunner);
				renaissanceDeck.getCardInstances().add(ciSnake);
				renaissanceDeck.getCardInstances().add(ciWildBore);
				renaissanceDeck.getCardInstances().add(ciWolf);
				
				// card instances -> deck
				ciBigfoot.getDecks().add(survivalDeck.getId());
				ciBullfrog.getDecks().add(survivalDeck.getId());
				ciCerberus.getDecks().add(survivalDeck.getId());
				ciElephant.getDecks().add(survivalDeck.getId());
				ciElfMage.getDecks().add(survivalDeck.getId());
				ciGoblin.getDecks().add(survivalDeck.getId());
				
				ciHedgehog.getDecks().add(renaissanceDeck.getId());
				ciMagicRock.getDecks().add(renaissanceDeck.getId());
				ciRoadrunner.getDecks().add(renaissanceDeck.getId());
				ciSnake.getDecks().add(renaissanceDeck.getId());
				ciWildBore.getDecks().add(renaissanceDeck.getId());
				ciWolf.getDecks().add(renaissanceDeck.getId());
			}
		}
		
		// add cards to list
		out.add(bigfoot);
		out.add(bullfrog);
		out.add(cerberus);
		out.add(elephant);
		out.add(elfMage);
		out.add(goblin);
		out.add(hedgehog);
		out.add(magicRock);
		out.add(roadrunner);
		out.add(snake);
		out.add(wildBore);
		out.add(wolf);
		
		// commit transactions
		em.getTransaction().commit();
		
		return out;
	}
	
	private void insertFreebieCards(EntityManager em, List<Card> cards, User tcgLocationUser) {
		// begin transaction
		em.getTransaction().begin();
		
		double sLat = RestConfig.FREEBIE_SOUTH_BOUNDARY_LAT;
		double wLon = RestConfig.FREEBIE_WEST_BOUNDARY_LON;
		double latDiff = RestConfig.FREEBIE_NORTH_BOUNDARY_LAT	- RestConfig.FREEBIE_SOUTH_BOUNDARY_LAT;
		double lonDiff = RestConfig.FREEBIE_EAST_BOUNDARY_LON	- RestConfig.FREEBIE_WEST_BOUNDARY_LON;
		
		int size = cards.size();
		
		Card c = null;
		CardInstance ci = null;
		double lat, lon;
		for (int i = 0; i < RestConfig.NUM_OF_FREEBIE_CIS; i++) {
			c = cards.get( (i % size) );
			
			lat = sLat + ( latDiff * Math.random() );
			lon = wLon + ( lonDiff * Math.random() );
			
			// create new freebie card instance
			ci = new CardInstance(lat, lon, tcgLocationUser.getId(), c.getId());
			
			// persist new freebie card instance
			em.persist(ci);
			em.flush();
			
			// user -> card instance
			tcgLocationUser.getCardInstances().add(ci);
			
			// card instance -> user
			ci.setUserId(tcgLocationUser.getId());
			//ci.setUser(tcgLocationUser);
			
			// card instance -> card
			ci.setCardId(c.getId());
			
			// card -> card instance
			c.getCardInstances().add(ci);
			
			// log
			log.debug("Created freebie card at position {}, {} ## Card: {} ({})", new Object[] { lat, lon, c.getName(), c.getId() });
		}
		
		// commit transaction
		em.getTransaction().commit();
	}
	
	private void insertFreebieCardAtPosition(EntityManager em, List<Card> cards, User tcgLocationUser, double lat, double lon) {
		// begin transaction
		em.getTransaction().begin();
		
		// Random Card
		int index = new Random(System.currentTimeMillis()).nextInt(cards.size());
		Card c = cards.get(index);
		
		// create new freebie card instance
		CardInstance ci = new CardInstance(lat, lon, tcgLocationUser.getId(), c.getId());
		
		// persist new freebie card instance
		em.persist(ci);
		em.flush();
		
		// user -> card instance
		tcgLocationUser.getCardInstances().add(ci);
		
		// card instance -> user
		ci.setUserId(tcgLocationUser.getId());
		//ci.setUser(tcgLocationUser);
		
		// card instance -> card
		ci.setCardId(c.getId());
		
		// card -> card instance
		c.getCardInstances().add(ci);
		
		// log
		log.debug("Created freebie card at position {}, {} ## Card: {} ({})", new Object[] { lat, lon, c.getName(), c.getId() });
		
		// commit transaction
		em.getTransaction().commit();
	}
}
